/*
 *  SyncIT , easy synchronization of files between computers and devices.
 *  Copyright (C) 2011  Christian Masus <cmasus(at)gmail.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version. If redistributing and/or modifying
 *  this file, this notice must be included in the redistributed or
 *  modified copy.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package log;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import config.ConfigInterface;

/**
 * Buffer for log messages so that the messages will be delivered in the
 * correct order, which is time order.
 * @author Christian Masus
 *
 */
public class LogBuffer implements LogInterface {

	private final int BUFFER_SIZE = 5;
	private final int TIMER_INTERVAL = 200;
	private final int TRIES_BEFORE_CANCEL = 50;

	private Log log;
	private LogLinkedList logBuffer;
	private Timer timer;
	private boolean timerTaskOn;

	public LogBuffer(ConfigInterface config, Timer timer) {
		log = new Log(config);
		logBuffer = new LogLinkedList();
		this.timer = timer;
		timerTaskOn = false;
	}

	public synchronized void log(Date date, String type, String message) {
		if(logBuffer.size() >= BUFFER_SIZE) { // Remove one element from the buffer
			deliverOneMessageUnsyched();
		}
		logBuffer.add(new LogMsg(date, type, message));

		if(timerTaskOn == false) { // Start the timer
			timerTaskOn = true;
			timer.schedule(new Task(), 0, TIMER_INTERVAL);
		}
	}

	public void logError(Date date, String message) {
		log(date, Log.LOG_ERROR, message);
	}

	public void logWarning(Date date, String message) {
		log(date, Log.LOG_WARNING, message);
	}

	public void logInfo(Date date, String message) {
		log(date, Log.LOG_INFO, message);
	}



	public void registerLogReceivers(String regName, LogRestrictions restrictions) {
		log.registerLogReceivers(regName, restrictions);
	}

	public void unregisterLogReceivers(String regName) {
		log.unregisterLogReceivers(regName);
	}

	public void changeLogReceiversRestrictions(String regName, LogRestrictions restrictions) {
		log.changeLogReceiversRestrictions(regName, restrictions);
	}

	public void askForLogHistory(String regName) {
		log.askForLogHistory(regName);
	}

	public void subscribeOnLog(String regName) {
		log.subscribeOnLog(regName);
	}
	public void stopLogSubscription(String regName) {
		log.stopLogSubscription(regName);
	}





	/**
	 * Delivers one message down to the Log instance.
	 * Warning: This method must be called from a synchronized method.
	 */
	private void deliverOneMessageUnsyched() {
		LogMsg logMsg = logBuffer.take();
		log.log(logMsg.getDate(), logMsg.getType(), logMsg.getMessage());
	}
	/**
	 * Delivers one message down to the Log instance.
	 */
	private synchronized void deliverOneMessage() {
		deliverOneMessageUnsyched();
	}



	public String toString() {
		String ret = "BUFFER_SIZE = " + BUFFER_SIZE + "\n";
		logBuffer.toString();
		return ret;
	}



	private class Task extends TimerTask {

		private int emptyRuns;
		public Task() {
			emptyRuns = 0;
		}

		public void run() {
			if(logBuffer.size() >= 1) {
				emptyRuns = 0;
				long msgTime = logBuffer.peekOldestTime();
				long timeDiff = scheduledExecutionTime() - msgTime;
				if(timeDiff >= TIMER_INTERVAL*2) {
					deliverOneMessage();
				}
			}
			else {
				++emptyRuns;
				if(emptyRuns >= TRIES_BEFORE_CANCEL) {
					emptyRuns = 0;
					timerTaskOn = false;
					cancel();
				}
			}
		}
	}

}
